#include "uart_bus.h"
#include "Comm.h"

struct tagCommBuff {
		OS_SEM semaphore;
		uint16_t size;
		uint16_t cout,qh,qt;
		uint8_t *buff;		
};

struct tagComm {
		struct tagCommBuff txBuff,rxBuff;
	   	struct tagUartAgreement agreement;
};

#define nl_COMM_BUFF				(nlUARTBUFF/2)
static struct tagComm COMM[nlCOMM];

static void Comm_Wait_Tc(struct tagComm *pComm)
{
    uint32_t i=0;
	struct tagCommBuff *pTxBuff = &(pComm->txBuff);
    
    if(os_sem_wait(pTxBuff->semaphore, 50) == OS_R_TMO)
    {
        UartBus_RxEn(pComm->agreement.uartbus);  
		
        return;
    }
    
	while (!UartBus_Tx_IsEnd(pComm->agreement.uartbus))
        {
			if ((i++) > 10000ul) break;
		}  
    
    UartBus_RxEn(pComm->agreement.uartbus);
}

static void comm_tx_handler(struct tagUartBus *pUb,void *parg)
{
		struct tagComm *pComm = (struct tagComm *)(pUb->agreement->prag);
		struct tagCommBuff *pTxBuff = &(pComm->txBuff);
		uint32_t i=0;
		
		if (pTxBuff->cout > 0) {
				UartBus_Write(pUb,pTxBuff->buff[pTxBuff->qt]);
				pTxBuff->qt = (pTxBuff->qt + 1) % pTxBuff->size;
				pTxBuff->cout--;
				isr_sem_send (pTxBuff->semaphore);
				return;		
		}

        do{
            i++;
        }while(i>100);
        
		UartBus_TxDis(pUb);
        isr_sem_send (pTxBuff->semaphore);
        
}

static __INLINE int32_t comm_write_byte(struct tagComm *pComm,const uint8_t data)
{
		struct tagCommBuff *pTxBuff = &(pComm->txBuff);
		
		os_sem_wait (pTxBuff->semaphore, 500);
		if (pTxBuff->cout >= pTxBuff->size) return -1;

		pTxBuff->buff[pTxBuff->qh] = data;
		pTxBuff->qh = (pTxBuff->qh + 1) % pTxBuff->size;
		pTxBuff->cout++;

		UartBus_TxEn(pComm->agreement.uartbus);
        Comm_Wait_Tc(pComm);

		return 0;
}

static void comm_rx_handler(struct tagUartBus *pUb,void *parg)
{
		struct tagComm *pComm = (struct tagComm *)(pUb->agreement->prag);
		struct tagCommBuff *pRxBuff = &(pComm->rxBuff);

		if (pRxBuff->cout < pRxBuff->size) {
				pRxBuff->buff[pRxBuff->qh] = UartBus_Read(pUb);
				pRxBuff->qh = (pRxBuff->qh + 1) % pRxBuff->size;
				pRxBuff->cout++;
				isr_sem_send (pRxBuff->semaphore);
		} else {
				(void)UartBus_Read(pUb);
		}
}

static __INLINE int32_t comm_read_byte(struct tagComm *pComm,uint8_t *pDat)
{
		struct tagCommBuff *pRxBuff = &(pComm->rxBuff);
		
		os_sem_wait (pRxBuff->semaphore, 500);
		if (pRxBuff->cout == 0) return -1;
		
		*pDat = pRxBuff->buff[pRxBuff->qt];
		pRxBuff->qt = (pRxBuff->qt + 1) % pRxBuff->size;
		pRxBuff->cout--;
		
		return 0;
}

static __INLINE int32_t comm_initialize(struct tagComm *pComm,struct tagUartBus *pUb)
{
		struct tagUartAgreement *pUagr = &(pComm->agreement);
		
		if (UartBus_LinkAgreement(pUb,pUagr,2000) == -1) return -1;
		
		UartBus_RxDis(pUb);
		UartBus_TxDis(pUb);
		
		pComm->txBuff.size = nl_COMM_BUFF;
		pComm->txBuff.cout = 0;
		pComm->txBuff.qh 	= 0;
		pComm->txBuff.qt 	= 0;
		pComm->txBuff.buff = pUagr->box;
		os_sem_init (pComm->txBuff.semaphore, nl_COMM_BUFF);

		pComm->rxBuff.size = nl_COMM_BUFF;
		pComm->rxBuff.cout = 0;
		pComm->rxBuff.qh 	= 0;
		pComm->rxBuff.qt 	= 0;
		pComm->rxBuff.buff = pUagr->box + nl_COMM_BUFF;
		os_sem_init (pComm->rxBuff.semaphore, 0);

		pUagr->tx_handler 	= comm_tx_handler;
		pUagr->rx_handler 	= comm_rx_handler;
		pUagr->prag 		= (void *)pComm;

		UartBus_RxEn(pUb);

		return 0;
}

static __INLINE void comm_release(struct tagComm *pComm)
{		
		struct tagUartAgreement *pUagr = &(pComm->agreement);
		struct tagUartBus *pUb = pUagr->uartbus;
		UartBus_UnLinkAgreement(pUb,&(pComm->agreement));
		
		UartBus_RxDis(pUb);
		UartBus_TxDis(pUb);
		
		pComm->txBuff.size = 0;
		pComm->txBuff.cout = 0;
		pComm->txBuff.qh 	= 0;
		pComm->txBuff.qt 	= 0;
		pComm->txBuff.buff = NULL;

		pComm->rxBuff.size = 0;
		pComm->rxBuff.cout = 0;
		pComm->rxBuff.qh 	= 0;
		pComm->rxBuff.qt 	= 0;
		pComm->rxBuff.buff = NULL;

		pUagr->tx_handler 	= NULL;
		pUagr->rx_handler 	= NULL;
		pUagr->prag 		= NULL;
}


int32_t Comm_Open(const COMMTYPE commId)
{
		return comm_initialize(COMM+commId,UarBus[commId]);		
}

void Comm_Close(const COMMTYPE commId)
{
		comm_release(COMM+commId);		
}

int32_t Comm_Write(const COMMTYPE commId,const uint16_t len,uint8_t *data)
{
		uint16_t i;

		for (i = 0; i < len; i++) {
			if (-1 == comm_write_byte(COMM+commId,data[i])) {
				break;
			}
		}
		
		return i;
}

int32_t Comm_Read(const COMMTYPE commId,const uint16_t len,uint8_t *data)
{
		uint16_t i;

		for (i = 0; i < len; i++) {
			if (-1 == comm_read_byte(COMM+commId,data+i)) {
				break;
			}
		}
		
		return i;
}
